package net.lab0.nebula.gui;

import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;

import net.lab0.nebula.color.AquaColorModel;
import net.lab0.nebula.color.ColorationModel;
import net.lab0.nebula.core.NebulabrotRenderer;
import net.lab0.nebula.core.QuadTreeManager;
import net.lab0.nebula.data.RawMandelbrotData;
import net.lab0.nebula.gui.enums.PowerOfTen;
import net.lab0.nebula.gui.enums.RenderingMethod;
import net.lab0.nebula.listener.MandelbrotRendererListener;
import net.lab0.tools.geom.Point;
import net.lab0.tools.geom.Rectangle;

import com.esr.carto.map2.ViewPort;
import com.esr.carto.map2.iface.ViewPortInterface;

@SuppressWarnings("serial")
public class RenderingPanel
extends JPanel
{
    private JPanel                     pnlNebulaPreview;
    private JComboBox<PowerOfTen>      cmbxPointsForPreview;
    private JProgressBar               progressBarRendering;
    private JComboBox<RenderingMethod> cmbxRenderingMethod;
    
    private MainWindow                 mainWindow;
    private Thread                     renderingThread;
    private JButton                    btnStart;
    private JButton                    btnStop;
    private NebulabrotRenderer         mandelbrotRenderer;
    
    // useful for window builder
    @SuppressWarnings("unused")
    private RenderingPanel()
    {
        this(null);
    }
    
    public RenderingPanel(MainWindow mainWindow)
    {
        this.mainWindow = mainWindow;
        
        this.setLayout(new BorderLayout(0, 0));
        
        pnlNebulaPreview = new JPanel();
        this.add(pnlNebulaPreview, BorderLayout.CENTER);
        
        JPanel panel_10 = new JPanel();
        this.add(panel_10, BorderLayout.SOUTH);
        GridBagLayout gbl_panel_10 = new GridBagLayout();
        gbl_panel_10.columnWidths = new int[] { 146, 0 };
        gbl_panel_10.rowHeights = new int[] { 17, 0 };
        gbl_panel_10.columnWeights = new double[] { 1.0, Double.MIN_VALUE };
        gbl_panel_10.rowWeights = new double[] { 0.0, Double.MIN_VALUE };
        panel_10.setLayout(gbl_panel_10);
        
        progressBarRendering = new JProgressBar();
        GridBagConstraints gbc_progressBarRendering = new GridBagConstraints();
        gbc_progressBarRendering.fill = GridBagConstraints.HORIZONTAL;
        gbc_progressBarRendering.anchor = GridBagConstraints.NORTH;
        gbc_progressBarRendering.gridx = 0;
        gbc_progressBarRendering.gridy = 0;
        panel_10.add(progressBarRendering, gbc_progressBarRendering);
        progressBarRendering.setStringPainted(true);
        
        JPanel panel_11 = new JPanel();
        this.add(panel_11, BorderLayout.WEST);
        GridBagLayout gbl_panel_11 = new GridBagLayout();
        gbl_panel_11.columnWidths = new int[] { 0, 0 };
        gbl_panel_11.rowHeights = new int[] { 0, 0, 0, 0, 0, 0 };
        gbl_panel_11.columnWeights = new double[] { 0.0, 1.0 };
        gbl_panel_11.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE };
        panel_11.setLayout(gbl_panel_11);
        
        JLabel lblStep = new JLabel("Points");
        GridBagConstraints gbc_lblStep = new GridBagConstraints();
        gbc_lblStep.anchor = GridBagConstraints.EAST;
        gbc_lblStep.insets = new Insets(0, 0, 5, 5);
        gbc_lblStep.gridx = 0;
        gbc_lblStep.gridy = 0;
        panel_11.add(lblStep, gbc_lblStep);
        
        cmbxPointsForPreview = new JComboBox<>();
        cmbxPointsForPreview.setModel(new DefaultComboBoxModel<PowerOfTen>(PowerOfTen.values()));
        GridBagConstraints gbc_cmbxPointsForPreview = new GridBagConstraints();
        gbc_cmbxPointsForPreview.insets = new Insets(0, 0, 5, 0);
        gbc_cmbxPointsForPreview.fill = GridBagConstraints.HORIZONTAL;
        gbc_cmbxPointsForPreview.gridx = 1;
        gbc_cmbxPointsForPreview.gridy = 0;
        panel_11.add(cmbxPointsForPreview, gbc_cmbxPointsForPreview);
        
        JLabel lblMethod = new JLabel("Method");
        GridBagConstraints gbc_lblMethod = new GridBagConstraints();
        gbc_lblMethod.anchor = GridBagConstraints.EAST;
        gbc_lblMethod.insets = new Insets(0, 0, 5, 5);
        gbc_lblMethod.gridx = 0;
        gbc_lblMethod.gridy = 1;
        panel_11.add(lblMethod, gbc_lblMethod);
        
        cmbxRenderingMethod = new JComboBox<>();
        cmbxRenderingMethod.setModel(new DefaultComboBoxModel<RenderingMethod>(RenderingMethod.values()));
        GridBagConstraints gbc_cmbxRenderingMethod = new GridBagConstraints();
        gbc_cmbxRenderingMethod.insets = new Insets(0, 0, 5, 0);
        gbc_cmbxRenderingMethod.fill = GridBagConstraints.HORIZONTAL;
        gbc_cmbxRenderingMethod.gridx = 1;
        gbc_cmbxRenderingMethod.gridy = 1;
        panel_11.add(cmbxRenderingMethod, gbc_cmbxRenderingMethod);
        
        btnStart = new JButton("Start");
        btnStart.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                updateNebulabrotPreview();
            }
        });
        GridBagConstraints gbc_btnStart = new GridBagConstraints();
        gbc_btnStart.fill = GridBagConstraints.HORIZONTAL;
        gbc_btnStart.insets = new Insets(0, 0, 5, 0);
        gbc_btnStart.gridwidth = 2;
        gbc_btnStart.gridx = 0;
        gbc_btnStart.gridy = 2;
        panel_11.add(btnStart, gbc_btnStart);
        
        JButton btnExport = new JButton("Export");
        btnExport.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                showRenderingExportFrame();
            }
        });
        
        btnStop = new JButton("Stop");
        btnStop.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                btnStop.setText("Stopping...");
                btnStop.setEnabled(false);
                new Thread()
                {
                    public void run()
                    {
                        mandelbrotRenderer.stopAndExit();
                        SwingUtilities.invokeLater(new Runnable()
                        {
                            @Override
                            public void run()
                            {
                                btnStop.setText("Stop");
                                btnStop.setEnabled(true);
                                btnStart.setEnabled(true);
                            }
                        });
                    };
                }.start();
            }
        });
        btnStop.setEnabled(false);
        GridBagConstraints gbc_btnStop = new GridBagConstraints();
        gbc_btnStop.fill = GridBagConstraints.HORIZONTAL;
        gbc_btnStop.gridwidth = 2;
        gbc_btnStop.insets = new Insets(0, 0, 5, 0);
        gbc_btnStop.gridx = 0;
        gbc_btnStop.gridy = 3;
        panel_11.add(btnStop, gbc_btnStop);
        GridBagConstraints gbc_btnExport = new GridBagConstraints();
        gbc_btnExport.fill = GridBagConstraints.HORIZONTAL;
        gbc_btnExport.gridwidth = 2;
        gbc_btnExport.gridx = 0;
        gbc_btnExport.gridy = 4;
        panel_11.add(btnExport, gbc_btnExport);
        
    }
    
    public void updateNebulabrotPreview()
    {
        
        final QuadTreeManager quadTreeManager = mainWindow.getQuadTreeManager();
        
        if (quadTreeManager == null)
        {
            mainWindow.showNoQuadTreeError();
            return;
        }
        
        btnStart.setEnabled(false);
        
        double aspectRatio = (pnlNebulaPreview.getWidth() + 0.1) / (pnlNebulaPreview.getHeight() + 0.1);
        ViewPortInterface viewPort = new ViewPort(new Rectangle(new Point(-2.0, -2.0), new Point(2.0, 2.0)));
        viewPort.setAspectRatio(aspectRatio);
        mandelbrotRenderer = new NebulabrotRenderer(pnlNebulaPreview.getWidth(), pnlNebulaPreview.getHeight(), viewPort);
        
        switch (cmbxRenderingMethod.getItemAt(cmbxRenderingMethod.getSelectedIndex()))
        {
            case LINEAR:
                renderingThread = new Thread("Mandelbrot renderer thread")
                {
                    public void run()
                    {
                        mandelbrotRenderer.linearRender(cmbxPointsForPreview.getItemAt(cmbxPointsForPreview.getSelectedIndex()).quantity, 100, 200);
                    };
                };
                break;
            
            case QUADTREE:
                renderingThread = new Thread("Mandelbrot renderer thread")
                {
                    public void run()
                    {
                        mandelbrotRenderer.quadTreeRender(cmbxPointsForPreview.getItemAt(cmbxPointsForPreview.getSelectedIndex()).quantity, 100, 200,
                        quadTreeManager.getQuadTreeRoot());
                    };
                };
                break;
            
            default:
                assert (false);
                break;
        }
        
        renderingThread.start();
        btnStop.setEnabled(true);
        
        final ColorationModel colorationModel = new AquaColorModel();
        
        mandelbrotRenderer.addMandelbrotRendererListener(new MandelbrotRendererListener()
        {
            @Override
            public void rendererProgress(long current, long total)
            {
                
                final long originalCurrent = current;
                final long originalTotal = total;
                while (total > Integer.MAX_VALUE)
                {
                    current /= 2;
                    total /= 2;
                }
                
                final int totalInt = (int) total;
                final int currentInt = (int) current;
                
                SwingUtilities.invokeLater(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        progressBarRendering.setMaximum(totalInt);
                        progressBarRendering.setValue(currentInt);
                        progressBarRendering.setString("" + originalCurrent + "/" + originalTotal);
                    }
                });
            }
            
            @Override
            public void rendererFinished(RawMandelbrotData raw)
            {
                final BufferedImage bufferedImage = raw.computeBufferedImage(colorationModel);
                
                SwingUtilities.invokeLater(new Runnable()
                {
                    
                    @Override
                    public void run()
                    {
                        updateGUI(bufferedImage, "Finished");
                    }
                });
            }
            
            @Override
            public void rendererStopped(RawMandelbrotData raw)
            {
                final BufferedImage bufferedImage = raw.computeBufferedImage(colorationModel);
                
                SwingUtilities.invokeLater(new Runnable()
                {
                    
                    @Override
                    public void run()
                    {
                        updateGUI(bufferedImage, "Stopped");
                    }
                });
            }
            
            private void updateGUI(final BufferedImage bufferedImage, String progressBarString)
            {
                ImageIcon icon = new ImageIcon(bufferedImage);
                pnlNebulaPreview.removeAll();
                pnlNebulaPreview.add(new JLabel(icon));
                pnlNebulaPreview.revalidate();
                progressBarRendering.setString(progressBarString);
                btnStart.setEnabled(true);
                btnStop.setEnabled(false);
            }
            
        });
        
    }
    
    public void showRenderingExportFrame()
    {
        JFrame frame = new ExportWindow(mainWindow);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
